#include "planet.h"
#include "physics/physicsworld.h"
#include "util/rand.h"
#include "math/coremath.h"

static const int RandSeed = 123;
static gecko::RandomNumberGenerator g_rand(RandSeed);

void Midpoint(std::vector<float>& heightmap, int low, int high, float offset = -1.0f)
{
	int mid = ((high - low) / 2) + low;

	// end condition
	if (mid == low || mid == high || mid == 0)
	{
		return;
	}

	//if (offset == -1.0f)
	//{
		//float midHeight = heightmap[high] - heightmap[low];

		//float offsetMin = heightmap[low];
		//float offsetMax = heightmap[high];
		//float halfOffset = (offsetMax - offsetMin) * 0.5f;

		//float randOffset = (gecko::Rand2<float>() * halfOffset) * 0.6f;
		//offset = offsetMin + halfOffset;// + randOffset;
	//}

	float midHeight = heightmap[low] + ((heightmap[high] - heightmap[low]) * 0.5f);

	float randOffset = gecko::Rand<float>() * 1.5f;
	heightmap[mid] = randOffset * (midHeight + offset);

	// recurse into left-hand half
	Midpoint(heightmap, low, mid, offset * 0.25f);

	// recurse into left-hand half
	Midpoint(heightmap, mid, high, offset * 0.25f);
}

std::vector<float> GenerateHeightmap(int size)
{
	// todo, make sure size is power of 2 plus 1

	std::vector<float> heightmap;

	// init
	for (int i = 0; i < size; i++)
	{
		heightmap.push_back(0.0f);
	}

	//int low = 0;
	//int high = size - 1;
	//Midpoint(heightmap, low, high, 1.0f);

	int halfSize = (size / 2) + 1;
	for (int i = 1; i < halfSize; i++)
	{
		heightmap[i] = heightmap[i - 1] + (gecko::Rand2<float>() * 0.05f);
		heightmap[i] = gecko::Max<float>(heightmap[i], 0.0f);
	}

	int j = halfSize - 1;
	for (int i = halfSize - 1; i < size; i++)
	{
		heightmap[i] = heightmap[j--];
	}



	return heightmap;
}

Planet::Planet()
{

}

void Planet::Initialise( gecko::IEffect* effect )
{
	CreateAtmosphere(effect);
	CreateTerrain(effect);
	CreateTerrain(effect);
	CreateInnerSphere(effect);

	rotationSpeed = gecko::Rand2<float>() * 0.05f;
	angle = 0.0f;
	rotationAxis = gecko::Vector3f(0.0f, 0.0f, 1.0f);//.Randomise();
	rotationAxis.Normalise();

	m_mass = 10.0f; //gecko::Rand<float>() * 100.0f;
	m_bodyForces.Zero();
}

void Planet::InitialisePhysics( gecko::PhysicsWorld & physicsWorld )
{
	float width = 15.0f;
	float height = 15.0f;
	gecko::Vector3f position(gecko::Rand2<float>() * width, gecko::Rand2<float>() * height, 0.0f);

	gecko::ShapeDef circleShape;
	circleShape.Type = gecko::ShapeDef::Type_Circle;
	circleShape.Radius = (float)RADIUS;
	circleShape.Density = 1.0f;
	circleShape.Offset = gecko::Vector3f();

	gecko::BodyDef bodyDef;
	bodyDef.BodyType = gecko::BodyDef::Type_Dynamic;
	bodyDef.Restitution = 0.1f;
	bodyDef.Friction = 1.0f;
	bodyDef.FixedRotation = false;
	bodyDef.SpriteOffset = gecko::Vector3f();
	bodyDef.Shapes[0] = circleShape;
	bodyDef.NumShapes = 1;

	// create body
	m_body = physicsWorld.CreatePhysicsObject(bodyDef, position);

	//m_body->SetLinearVelocity(b2Vec2(2.0f, 0.0f));
	m_body->m_body->SetAngularVelocity(0.1f);
}

gecko::Entity::UpdateReturn Planet::Update( float dt )
{
	//velocity += bodyForces;
	//velocity.z = 0.0f;

	//m_position += velocity * dt;
	//m_position.z = -10.0f;

	//b2CircleShape * shape = (b2CircleShape*)m_body->GetFixtureList()->GetShape();
	//b2Vec2 center = m_body->GetWorldPoint(shape->m_p);

	//m_position = gecko::Vector3f(center.x, center.y, 0.0f);



	gecko::Matrix44 rotation;
	rotation.SetupRotation(rotationAxis, m_body->GetRotation());

	gecko::Matrix44 translation;
	gecko::Vector3f myPosition;
	m_body->GetPosition(myPosition);
	translation.SetupTranslation(myPosition);

	for (gecko::gtl::Vector<gecko::DynamicGeometryInstance*>::Iterator itr = m_geometryInstances.Begin(); itr != m_geometryInstances.End(); ++itr)
	{
		(*itr)->Tranform = rotation * translation;
	}

	//angle += rotationSpeed * dt;

	//bodyForces.Zero();

	return GameEntity::Update(dt);
}

void Planet::Render( gecko::IRenderer* renderer )
{
	for (gecko::gtl::Vector<gecko::DynamicGeometryInstance*>::Iterator itr = m_geometryInstances.Begin(); itr != m_geometryInstances.End(); ++itr)
	{
		renderer->AddBatchedGeometry((*itr));
	}
}

void Planet::CreateTerrain( gecko::IEffect* effect )
{
	float heightMap[MAX_SEGMENTS];

	for (int i = 0; i < MAX_SEGMENTS ; i++)
	{
		heightMap[i] = RADIUS + (g_rand.GenerateNormalised() * 0.2f);
	}

	gecko::Colour randomColour(g_rand.GenerateNormalised(), g_rand.GenerateNormalised(), g_rand.GenerateNormalised(), 1.0f);

	static gecko::Vertex verts[MAX_SEGMENTS + 1];

	gecko::Vector3f position;
	verts[0].Position = position; verts[0].Colour = randomColour.ToInt();//gecko::Colour::Black().ToInt();

	for (int i = 0; i < MAX_SEGMENTS + 1; i++)
	{
		float angle = (gecko::TWO_PI / MAX_SEGMENTS);
		verts[i + 1].Position = gecko::Vector3f(heightMap[i] * cosf(i * angle), heightMap[i] * sinf(i * angle), 0.0f); verts[i+1].Colour = randomColour.ToInt();
	}

	static unsigned short indices[MAX_SEGMENTS * 3];
	unsigned short index = 1;
	for (unsigned short i = 0; i < MAX_SEGMENTS * 3; i += 3)
	{
		indices[i] = 0;
		indices[i + 1] = index++;
		indices[i + 2] = index;
	}

	indices[(MAX_SEGMENTS * 3) - 1] = 1;

	gecko::Geometry* mesh = new gecko::Geometry;
	mesh->Initialise(verts, MAX_SEGMENTS + 1, indices, MAX_SEGMENTS *3, effect, gecko::IVertexBuffer::PT_TRIANGLELIST);

	gecko::DynamicGeometryInstance* instance = new gecko::DynamicGeometryInstance;
	instance->m_Geometry = mesh;

	m_geometryInstances.PushBack(instance);
}

void Planet::CreateInnerSphere( gecko::IEffect* effect )
{
	gecko::Colour colour = gecko::Colour::Black();

	gecko::Vector3f position;
	static gecko::Vertex verts[MAX_SEGMENTS + 1];
	verts[0].Position = position; verts[0].Colour = gecko::Colour::Black().ToInt();

	for (int i = 0; i < MAX_SEGMENTS + 1; i++)
	{
		float angle = (gecko::TWO_PI / MAX_SEGMENTS);
		verts[i + 1].Position = gecko::Vector3f(RADIUS * cosf(i * angle), RADIUS * sinf(i * angle), 0.0f); verts[i+1].Colour = colour.ToInt();
	}

	static unsigned short indices[MAX_SEGMENTS * 3];
	unsigned short index = 1;
	for (unsigned short i = 0; i < MAX_SEGMENTS * 3; i += 3)
	{
		indices[i] = 0;
		indices[i + 1] = index++;
		indices[i + 2] = index;
	}

	indices[(MAX_SEGMENTS * 3) - 1] = 1;

	gecko::Geometry* mesh = new gecko::Geometry;
	mesh->Initialise(verts, MAX_SEGMENTS + 1, indices, MAX_SEGMENTS * 3, effect, gecko::IVertexBuffer::PT_TRIANGLELIST);

	gecko::DynamicGeometryInstance* instance = new gecko::DynamicGeometryInstance;
	instance->m_Geometry = mesh;

	m_geometryInstances.PushBack(instance);
}

void Planet::CreateAtmosphere( gecko::IEffect* effect )
{
	gecko::Colour outerColour = gecko::Colour(1.0f, 0.0f, 0.0f, 0.0f);
	gecko::Colour innerColour = gecko::Colour(1.0f, 0.0f, 0.0f, 1.0f);

	float atmosHeight = RADIUS * 2.0f;

	gecko::Vector3f position;
	static gecko::Vertex verts[MAX_SEGMENTS + 1];
	verts[0].Position = position; verts[0].Colour = innerColour.ToInt();

	for (int i = 0; i < MAX_SEGMENTS + 1; i++)
	{
		float angle = (gecko::TWO_PI / MAX_SEGMENTS);
		verts[i + 1].Position = gecko::Vector3f(atmosHeight * cosf(i * angle), atmosHeight * sinf(i * angle), 0.0f); verts[i+1].Colour = outerColour.ToInt();
	}

	static unsigned short indices[MAX_SEGMENTS * 3];
	unsigned short index = 1;
	for (unsigned short i = 0; i < MAX_SEGMENTS * 3; i += 3)
	{
		indices[i] = 0;
		indices[i + 1] = index++;
		indices[i + 2] = index;
	}

	indices[(MAX_SEGMENTS * 3) - 1] = 1;

	gecko::Geometry* mesh = new gecko::Geometry;
	mesh->Initialise(verts, MAX_SEGMENTS + 1, indices, MAX_SEGMENTS *3, effect, gecko::IVertexBuffer::PT_TRIANGLELIST);

	gecko::DynamicGeometryInstance* instance = new gecko::DynamicGeometryInstance;
	instance->m_Geometry = mesh;

	m_geometryInstances.PushBack(instance);
}
